#ifndef csv2sql_h
#define csv2sql_h 1

// maximum length of all column names, each null-terminated
#ifndef C2S_COLNAMEDATA_MAX
#define C2S_COLNAMEDATA_MAX   1024
#endif
#define C2S_COLNAMEDATA_ERR   "Error: column names are too long.\n"

// operator overloads for flag enums
#define C2S_FLAGS(T) \
inline static T  operator *  (bool l, T r) \
{ return static_cast<T>(l*+r); } \
inline static T  operator *  (T l, bool r) \
{ return static_cast<T>(+l*r); } \
inline static T  operator |  (T l, T r) \
{ return static_cast<T>(+l|r); } \
inline static T  operator &  (T l, T r) \
{ return static_cast<T>(+l&r); } \
inline static T  operator +  (T l, T r) \
{ return l|r; } \
inline static T  operator -  (T l, T r) \
{ return static_cast<T>(+l-(l&r)); } \
inline static T& operator |= (T& l, T r) \
{ return l = l|r; } \
inline static T& operator &= (T& l, T r) \
{ return l = l&r; } \
inline static T& operator += (T& l, T r) \
{ return l = l+r; } \
inline static T& operator -= (T& l, T r) \
{ return l = l-r; }

namespace csv2sql
{

  #include <stdio.h>
  #include <string.h>

  class Options;
  class Parser;
  class IWriter;
  class SqlWriter;
  class Scanner;
  class ScanResult;
  
  
  enum ParserMode
  {
    PM_NONE  = 0,     // 0
    PM_READY = 1,     // ready for next col or row, or EOF
    PM_EMPTY = 2,     // empty cell
    PM_CELL =  4,     // cell
    PM_QCELL = 8,     // quoted cell
    PM_HEAD = 16,     // header cell
  };
  C2S_FLAGS(ParserMode);
  
  enum DataType
  {
    DT_NONE =     0,
    DT_STRING =   1,
    DT_DECIMAL =  2,
    DT_INTEGER =  4,
    DT_DATE =     8,
    DT_ANY =     15,
  };
  C2S_FLAGS(DataType);

  // command line arguments
  class Options
  {
  public:
    char *file, *col_end, *row_end, *quote, *escape, *table;
    Options();
    Options(int argc, char **argv);
    bool IsOk();
  private:
    bool isOk;
    void applyDefaults();
  };

  // size of a string (left) or decimal (left and right)
  struct DataSize
  {
  public: 
    int Left, Right;
    DataSize(){Left = Right = -1;}
  };

  // An IWriter's methods are called from Parser
  class IWriter
  {
  public:
    virtual bool Write(int c, ParserMode m)=0;
    virtual bool StartCell(ParserMode m)=0;
    virtual bool EndCell(ParserMode m)=0;
    virtual bool StartRow(ParserMode m)=0;
    virtual bool EndRow(ParserMode m)=0;
    virtual bool Start()=0;
    virtual bool End()=0;

  };

  // parses CSV
  class Parser
  {
  public:
    Parser(Options *o, IWriter *w);
    Parser(Options *o, IWriter *w, ScanResult *sr);
    bool Parse();
    void Abort();
  private:
    Options *options;
    ParserMode mode;
    IWriter *writer;
    char currentQuote;
    bool abort;
    void init();
  };

  class Scanner
  {
  public:
    Scanner(Options *o);
    virtual ~Scanner();
    ScanResult *Scan();
  private:
    Options *options;
    ScanResult *currentResult;
  };

  class ScanResult : public virtual IWriter
  {
  public:
    ScanResult();
    ScanResult(Options *o);
    virtual ~ScanResult();
    int ColCount;
    char **ColNames;
    DataType *ColTypes;
    DataSize *ColSizes;
  // SqlWriter methods
    bool Write(int c, ParserMode m);
    bool StartCell(ParserMode m);
    bool EndCell(ParserMode m);
    bool StartRow(ParserMode m);
    bool EndRow(ParserMode m);
    bool Start();
    bool End();
  private:
    Options *options;
    char colNameData[C2S_COLNAMEDATA_MAX];
    int colNameIndex, colIndex, colLength, decimalPos;
    void init();
    void adjustColSize();
  };
 
  class SqlWriter : public virtual IWriter
  {
  public:
    SqlWriter(Options *o);
    SqlWriter();
    bool WriteFile();
  // SqlWriter methods
    bool Write(int c, ParserMode m);
    bool StartCell(ParserMode m);
    bool EndCell(ParserMode m);
    bool StartRow(ParserMode m);
    bool EndRow(ParserMode m);
    bool Start();
    bool End();
  private:
    Options *options;
    ScanResult *meta;
    int rowNum, colNum;
  };
  
  
}

#endif

